/**
 * \file mm/mm.c
 * \author Davide Gessa
 * \note Gestione della memoria
 * \date 10-12-09
 */
#include <mm.h>
#include <video.h>
#include <debug.h>
#include <util.h>
#include "phys.h"
#include "paged.h"


/**
 * Inizializza il gestore della memoria
 * 
 * \param low memoria bassa
 * \param high memoria alta
 */
void mm_init(uint32_t low, uint32_t high)
{
	DEBUGP("mm_init()\n");
	
	unsigned x;
	
	/*
	 * Gestore fisico
	 */
	phys_total = low + high;
	
	// Calcoliamo il numero delle pagine
	phys_page_number = ((((unsigned) phys_total / PHYS_PAGE_SIZE)/32)*32);
	
	// Azzeriamo la bitmap
	for(x = 0; x < phys_page_number/32; x++)
			phys_bitmap[x] = 0;
			
	// Azzera la tabella delel allocazioni
	for(x = 0; x < PHYS_MAX_BLOCKS; x++)
		phys_allocated[x].dim = 0;
	
	// Area per rom, etc (primo mb)
	phys_reserve(0x00000000, 0x000FFFFF);
	// Riservata per i moduli
	phys_reserve(0x00100000, 0x003FFFFF);
	// Possibile uso per hardware mappati
	phys_reserve(0x00F00000, 0x00FFFFFF);
	// Usata dai driver ( a volte )
	phys_reserve(0x0C000000, 0x0FFFFFFF); 
	
	//video_putint(mm_get_used_mem());
	//video_puts("/");
	//video_putint(mm_get_total_mem());
	
	
	/*
	 * Gestore memoria paginata
	 */

	//paged_enable();
	
}



/**
 * Alloca una pagina per il kernel
 * 
 * \param size pagine da allocare
 * \return puntatore all'area allocata
 */
void *mm_kalloc(size_t size)
{
	DEBUGP("mm_kalloc()\n");
	
	unsigned bnum = (unsigned) ((size + 1) / (PHYS_PAGE_SIZE * 1024)) + 1; 
	// / PHYS_PAGE_SIZE + 1;
	//video_putint(bnum);
	//video_puts("\n");
	return (void *)phys_alloc(bnum);
}


/**
 * Libera una pagina per il kernel
 * 
 * \param addr indirizzzo della pagina
 */
void mm_kfree(void *addr)
{
	DEBUGP("mm_kfree()\n");
	
	phys_free(addr);
}


/**
 * Restituisce la memoria totale del sistema (kb)
 * 
 * \return memoria totale
 */
uint32_t mm_get_total_mem(){ return phys_total; }


/**
 * Restituisce la memoria usata del sistema (kb)
 * 
 * \return memoria usata
 */
uint32_t mm_get_used_mem()
{ 
	return ((phys_page_number - phys_get_free_pages()) * PHYS_PAGE_SIZE);
}



/**
 * Effettua un test sull'allocazione e deallocazione fisica e virtuale
 */
void mm_test()
{
	show_used_memory();
	video_puts("Alloc 1000 blocks of 4*1024 bytes\n");
	char *data = phys_alloc(1000);
	show_used_memory();
	video_puts("Delloc 1000 blocks of 4*1024 bytes\n");
	phys_free(data);
	show_used_memory();	
}


/**
 * Alloca e restituisce l'indirizzo di un blocco di memoria
 * mappato 1 ad 1 con la memoria fisica
 *
 */
void *mm_get_mem(size_t size)
{
	void *real = phys_alloc(size);
	
	page_map_page(real, real, 0x0);
	
	return real;
}
